/*
 * list.c
 *
 *  Created on: Apr 13, 2011
 *      Author: Gilad & Yotam
 */

#include <stdlib.h>
#include <stdbool.h>
#include "list.h"

typedef bool FLAG;

typedef struct node_t {
	ListElement data;
	struct node_t* next;
}*Node;

struct List_t {
	Node head;
	Node current;
	CopyListElement copyFunction;
	FreeListElement freeFunction;
};

/*********************Beginning of the static functions*********************/

/**
 * listPointLast: makes the list->current point to the last node of the list
 * @param list- the list to manipulate.
 */
static void listPointLast(List list) {
	Node tempIterator = list->head;
	while (tempIterator) {
		list->current = tempIterator;
		tempIterator = tempIterator->next;
	}
}

/**
 * nodeCreateNew: creates a new node to be inserted to the list
 * @param element the node's data to be linked to it.
 * @return
 * the created Node. NULL if malloc failed.
 */
static Node nodeCreateNew(ListElement element) {
	Node newNode = malloc(sizeof(struct node_t));
	if (newNode == NULL) {
		return NULL;
	}
	newNode->data = element;
	newNode->next = NULL;
	return newNode;
}

/**
 * listDestroyNodes: recursively destroys all the nodes in a list.
 * @param list - the list to destroy its nodes.
 * @param node - point to the current node for destruction.
 */
static void listDestroyNodes(List list, Node node) {
	if (node == NULL) {
		return;
	}
	listDestroyNodes(list, node->next);
	/*using the iterator and the listRemoveCurrent to delete the current node*/
	list->current = node;
	listRemoveCurrent(list);
}

/**
 * listGetPrevious: returns a pointer to the previous
 * node in the list according to the supplied node
 * @param list - the relevant list
 * @param suppliedNode-the supplied node to find its previous
 * @return
 * the previous node if there is one,
 * NULL if this node is the first one in the list
 */
static Node listGetPrevious(List list, Node suppliedNode) {
	if (list->head == suppliedNode) {
		return NULL;
	}
	Node tempIterator = list->head;
	FLAG nodeFound = false;
	while ((tempIterator) && (!nodeFound)) {
		if (tempIterator->next == suppliedNode) {
			nodeFound = true;
		} else {
			tempIterator = tempIterator->next;
		}
	}
	return tempIterator;
}

/**
 *	swapNodesData: swaps the data between two nodes in the list
 *	@param firstNode - the first node
 *	@param secondNode - the second node
 */
static void swapNodesData(Node firstNode, Node secondNode) {
	ListElement tempElement = firstNode->data;
	firstNode->data = secondNode->data;
	secondNode->data = tempElement;
}

/**
 * listBubbleSort: sorts the list without dynamic memory allocation
 * using a binary sort algorithem.
 * The function assumes the pointers have been checked and are not NULL.
 * and that the list was checked and one element is to be found in it.
 * @param list- the list to sort
 * @param compareElement - the compare function supplied by the user.
 */
static void listBubbleSort(List list, CompareListElements compareElement) {
	FLAG swapsWereMade = true;
	while (swapsWereMade) {
		swapsWereMade = false;
		Node leadNode = list->head;
		Node followingNode = leadNode->next;
		while (followingNode != NULL) {
			if ((compareElement((leadNode->data), (followingNode->data))) > 0) {
				swapNodesData(leadNode, followingNode);
				swapsWereMade = true;
			}
			leadNode = leadNode->next;
			followingNode = followingNode->next;
		}
	}
}

/************************End of the static functions************************/

List listCreate(CopyListElement copyElement, FreeListElement freeElement) {
	if ((copyElement == NULL) || (freeElement == NULL)) {
		return NULL;
	}
	List newList = malloc(sizeof(struct List_t));
	if (newList == NULL) {
		return NULL;
	}
	newList->head = NULL;
	newList->current = NULL;
	newList->copyFunction = copyElement;
	newList->freeFunction = freeElement;
	return newList;
}

List listCopy(List list) {
	List newList = listCreate(list->copyFunction, list->freeFunction);
	if (newList == NULL) {
		return NULL;
	}
	ListResult listResult = LIST_SUCCESS;
	Node sourceNode = list->head;
	while ((listResult == LIST_SUCCESS) && (sourceNode)) {
		listResult = listInsertLast(newList, sourceNode->data);
		if ((list->current == sourceNode) && (listResult == LIST_SUCCESS)) {
			listPointLast(newList);
			/*turns the iterator to point to the last node*/
		}
		sourceNode = sourceNode->next;
	}
	if (listResult == LIST_OUT_OF_MEMORY) {
		listDestroy(newList);
		return NULL;
	}
	return newList;
}

int listGetSize(List list) {
	if (list == NULL) {
		return 0;
	}
	int nodesCounter = 0;
	Node tempIterator = list->head;
	while (tempIterator) {
		nodesCounter++;
		tempIterator = tempIterator->next;
	}
	return nodesCounter;
}

ListElement listGetFirst(List list) {
	if ((list == NULL) || (list->head == NULL)) {
		return NULL;
	}
	list->current = list->head;
	return list->current->data;
}

ListElement listGetNext(List list) {
	if ((list == NULL) || (list->current == NULL)) {
		return NULL;
	}
	list->current = list->current->next;
	if (list->current == NULL) {
		return NULL;
	}
	return list->current->data;
}

ListElement listGetCurrent(List list) {
	if ((list == NULL) || (list->current == NULL)) {
		return NULL;
	}
	return list->current->data;
}

ListResult listInsertFirst(List list, ListElement element) {
	if ((list == NULL) || (element == NULL)) {
		return LIST_NULL_ARGUMENT;
	}
	ListElement elementCopy = list->copyFunction(element);
	if (elementCopy != NULL) {
		Node newNode = nodeCreateNew(elementCopy);
		if (newNode != NULL) {
			newNode->next = list->head;
			list->head = newNode;
			return LIST_SUCCESS;
		} else { /* couldn't create a new node */
			list->freeFunction(elementCopy);
		}
	}
	/* couldn't create a copy of the element
	 * or couldn't create a new node
	 */
	return LIST_OUT_OF_MEMORY;
}

ListResult listInsertLast(List list, ListElement element) {
	if ((list == NULL) || (element == NULL)) {
		return LIST_NULL_ARGUMENT;
	}
	if (list->head == NULL) {
		return listInsertFirst(list, element);
	}
	Node currentReserver = list->current;
	listPointLast(list);

	ListResult resultHolder = listInsertAfterCurrent(list, element);
	list->current = currentReserver;
	return resultHolder;
}

ListResult listInsertBeforeCurrent(List list, ListElement element) {
	if ((list == NULL) || (element == NULL)) {
		return LIST_NULL_ARGUMENT;
	}
	if (list->current == NULL) {
		return LIST_INVALID_CURRENT;
	}
	if (list->head == list->current) {
		return listInsertFirst(list, element);
	}
	ListElement elementCopy = list->copyFunction(element);
	if (elementCopy != NULL) {
		Node newNode = nodeCreateNew(elementCopy);
		if (newNode != NULL) {
			newNode->next = list->current;
			Node previousNode = listGetPrevious(list, list->current);
			previousNode->next = newNode;
			return LIST_SUCCESS;
		} else { /* couldn't create a new node */
			list->freeFunction(elementCopy);
		}
	}
	/* couldn't create a copy of the element
	 * or couldn't create a new node
	 */
	return LIST_OUT_OF_MEMORY;
}

ListResult listInsertAfterCurrent(List list, ListElement element) {
	if ((list == NULL) || (element == NULL)) {
		return LIST_NULL_ARGUMENT;
	}
	if (list->current == NULL) {
		return LIST_INVALID_CURRENT;
	}
	ListElement elementCopy = list->copyFunction(element);
	if (elementCopy != NULL) {
		Node newNode = nodeCreateNew(elementCopy);
		if (newNode != NULL) {
			newNode->next = list->current->next;
			list->current->next = newNode;
			return LIST_SUCCESS;
		} else { /* couln't create a new node */
			list->freeFunction(elementCopy);
		}
	}
	/* couldn't create a copy of the element
	 * or couldn't create a new node
	 */
	return LIST_OUT_OF_MEMORY;
}

ListResult listRemoveCurrent(List list) {
	if (list == NULL) {
		return LIST_NULL_ARGUMENT;
	}
	if (list->current == NULL) {
		return LIST_INVALID_CURRENT;
	}

	Node nodeToRemove = list->current;
	Node previousNode = listGetPrevious(list, list->current);
	if (previousNode == NULL) {
		list->head = nodeToRemove->next;
	} else {
		previousNode->next = nodeToRemove->next;
	}

	list->freeFunction(nodeToRemove->data);
	free(nodeToRemove);
	list->current = NULL;
	return LIST_SUCCESS;
}

ListResult listSort(List list, CompareListElements compareElement) {
	if ((list == NULL) || (compareElement == NULL)) {
		return LIST_NULL_ARGUMENT;
	}
	int listSize = listGetSize(list);
	if (listSize <= 1) {
		return LIST_SUCCESS;
	}
	/* There are at least 2 elements in the list */
	listBubbleSort(list, compareElement);
	return LIST_SUCCESS;
}

ListResult listClear(List list) {
	if (list == NULL) {
		return LIST_NULL_ARGUMENT;
	}
	if (list->head != NULL) {
		listDestroyNodes(list, list->head);
	}
	return LIST_SUCCESS;
}

List listFilter(List list, FilterListElement filterElement) {
	if (list == NULL || (filterElement == NULL)) {
		return NULL;
	}
	List filteredList = listCreate(list->copyFunction, list->freeFunction);
	if (filteredList == NULL) {
		return NULL;
	}
	Node tempIterator = list->head;
	ListResult creationResult = LIST_SUCCESS;
	while ((tempIterator) && (creationResult == LIST_SUCCESS)) {
		if (filterElement(tempIterator->data)) {
			creationResult = listInsertLast(filteredList, tempIterator->data);
			if (creationResult != LIST_SUCCESS) {
				listDestroy(filteredList);
				return NULL;
			}
		}
		tempIterator = tempIterator->next;
	}
	return filteredList;
}

void listDestroy(List list) {
	if (list == NULL) {
		return;
	}
	listDestroyNodes(list, list->head);
	free(list);
}
